/*
Copyright (C) 2011 by the PSVN Research Group, University of Alberta
*/

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <assert.h>
#include "abst_desc.hpp"

using namespace std;

AbstractionDescription::AbstractionDescription()
    : m_so_handle(NULL),
      m_game(NULL),
      m_abstraction(NULL)
{
}

void AbstractionDescription::GetStateSpaceName(const char* in, char* out)
{
    const char* str = strrchr(in, '/');
    if (*str == 0)
        str = in;
    str++;
    while (*str && (isalpha(*str) || isdigit(*str) || *str=='_')) {
        *out++ = *str++;
    }
    *out = 0;
}

std::vector<AbstractionDescription> 
AbstractionDescription::Load(const string& filename)
{
    vector<string> names;
    FILE* fs = fopen( filename.c_str(), "r" );
    if (!fs) {
        fprintf(stderr, "Could not open '%s' for reading!\n", filename.c_str());
        exit(EXIT_FAILURE);
    }
    char str[1024];
    while (!feof(fs)) {
        if (fscanf(fs, " %s ", str) != 1)
            break;
        names.push_back(string(str));
    }
    fclose(fs);
    
    if (names.size() % 2 != 1) {
        fprintf(stderr, "Hierarchy does not contain an odd number of names!\n");
        exit(EXIT_FAILURE);
    }
            
    vector<AbstractionDescription> ret;
    for (size_t i = 0; i < names.size(); i += 2) {
        AbstractionDescription cur;
        cur.m_so_handle = dlopen(names[i].c_str(), RTLD_LAZY);
        if(cur.m_so_handle == NULL) {
            fprintf(stderr, "could not open shared object %s: %s\n",
                    names[i].c_str(), dlerror());
            exit(EXIT_FAILURE);
        }
        // read the psvn state space object.
        // start by guessing its name, then checking for the default name.
        char name[1024];
        GetStateSpaceName(names[i].c_str(), name);
        cur.m_game = (const compiled_game_so_t*)dlsym(cur.m_so_handle, name);
        if(cur.m_game == NULL) {
            cur.m_game = (const compiled_game_so_t*)dlsym(cur.m_so_handle, 
                                                          "psvn_state_space");
            if (cur.m_game == NULL) {
                fprintf(stderr, "could not read game object from %s\n", name);
                exit(EXIT_FAILURE);
            }
        }
        // load abstraction for all but the highest level
        if (i < names.size() - 1) {
            printf("abstraction name=%s\n", names[i+1].c_str());
            cur.LoadAbstraction(names[i+1].c_str());
        }
        ret.push_back(cur);
    }
    return ret;
}

void AbstractionDescription::Load(const compiled_game_so_t* game)
{
    m_so_handle = NULL;
    m_game = game;
    m_abstraction = NULL;
}

void AbstractionDescription::Load(const compiled_game_so_t* game,
                                  const char* filename)
{
    m_so_handle = NULL;
    m_game = game;
    LoadAbstraction(filename);
}


void AbstractionDescription::LoadAbstraction(const char* filename)
{
    m_abstraction = read_so_abstraction_from_file(filename, m_game);
    if (m_abstraction == NULL) {
        fprintf(stderr, "cound not parse abstraction from %s\n", 
                        filename);
        exit(EXIT_FAILURE);
    }
}
